#include "../../../include/graphics/gl/GLShaderProgram.hpp"
#include "../../../include/graphics/Shader.hpp"
#include "../../../include/graphics/gl/GLShader.hpp"
#include "../../../include/error/Exception.hpp"
using namespace klas;
using namespace graphics;
using namespace gl;

GLShaderProgram::GLShaderProgram()
{
  m_programID = glCreateProgram();
}

GLShaderProgram::~GLShaderProgram()
{}

void GLShaderProgram::attach(Shader* shader)
{
  glAttachShader(m_programID, static_cast<GLShader*>(shader)->getShaderID());
}

void GLShaderProgram::deattach(Shader* shader)
{
  glDetachShader(m_programID, static_cast<GLShader*>(shader)->getShaderID());
}

void GLShaderProgram::link()
{
  glLinkProgram(m_programID);
}

void GLShaderProgram::enable()
{
  glUseProgram(m_programID);
}

void GLShaderProgram::disable()
{
  glUseProgram(0);
}



uint32 GLShaderProgram::setVariable(string name, double v0)
{
  return setVariable(getID(name), v0);
}

uint32 GLShaderProgram::setVariable(string name, double v0, double v1)
{
  return setVariable(getID(name), v0, v1);
}

uint32 GLShaderProgram::setVariable(string name, double v0, double v1, double v2)
{
  return setVariable(getID(name), v0, v1, v2);
}

uint32 GLShaderProgram::setVariable(string name, double v0, double v1, double v2, double v3)
{
  return setVariable(getID(name), v0, v1, v2, v3);
}

uint32 GLShaderProgram::setVariable(uint32 id, double v0)
{
  glUniform1f(id, float(v0));
  return id;
}

uint32 GLShaderProgram::setVariable(uint32 id, double v0, double v1)
{
  glUniform2f(id, float(v0), float(v1));
  return id;
}

uint32 GLShaderProgram::setVariable(uint32 id, double v0, double v1, double v2)
{
  glUniform3f(id, float(v0), float(v1), float(v2));
  return id;
}

uint32 GLShaderProgram::setVariable(uint32 id, double v0, double v1, double v2, double v3)
{
  glUniform4f(id, float(v0), float(v1), float(v2), float(v3));
  return id;
}

uint32 GLShaderProgram::getID(string name)
{
  return glGetUniformLocation(m_programID, name.c_str());
}

uint32 GLShaderProgram::setVariable(std::string name, uint8 elementCount, int totalElements, double *arr)
{
  return setVariable(getID(name), elementCount, totalElements, arr);
}

uint32 GLShaderProgram::setVariable(uint32 id, uint8 elementCount, int totalElements, double* arr)
{
  switch(elementCount)
  {
  case uint8(1):
    glUniform1fv(id, totalElements, reinterpret_cast<float*>(arr));
    break;
  case uint8(2):
    glUniform2fv(id, totalElements, reinterpret_cast<float*>(arr));
    break;
  case uint8(3):
    glUniform3fv(id, totalElements, reinterpret_cast<float*>(arr));
    break;
  case uint8(4):
    glUniform4fv(id, totalElements, reinterpret_cast<float*>(arr));
    break;
  default:
    throw error::Exception("GLShaderProgram::setVariable(uint32 id, int elementCount, int totalElements, double* arr) recieved an invalid element count.");
  }

  return id;
}
/*void GLShaderProgram::addVariable(string name, float v0)
{
  m_data[name] = glGetAttribLocation(m_programID, name.c_str());
  glVertexAttrib1f(m_data[name], v0);
}

void GLShaderProgram::addVariable(string name, float v0, float v1)
{
  m_data[name] = glGetAttribLocation(m_programID, name.c_str());
  glVertexAttrib2f(m_data[name], v0, v1);
}

void GLShaderProgram::addVariable(string name, float v0, float v1, float v2)
{
  m_data[name] = glGetAttribLocation(m_programID, name.c_str());
  glVertexAttrib3f(m_data[name], v0, v1, v2);
}

void GLShaderProgram::addVariable(string name, float v0, float v1, float v2, float v3)
{
  m_data[name] = glGetAttribLocation(m_programID, name.c_str());
  glVertexAttrib4f(m_data[name], v0, v1, v2, v3);
}*/